package client;

import java.util.ArrayList;
import java.util.Random;

/**
 * 
 */

/**
 * @author Sunny
 *
 */
public class Maze {
	
	public static final String south = "S";
	public static final String north = "N";
	public static final String west = "W";
	public static final String east = "E";
	public static final String noMove = "NM";
	
	private int length = 0;
	private int numOfTreasures = 0;
	private int treaAvailable = 0;
	private int[][] treasureLoc = null;
	private ArrayList<Player> players = new ArrayList<Player>();
	private boolean isPlaying = false;
	
	public Maze(int l, int treasures)
	{
		length = l;
		numOfTreasures = treasures;
		treaAvailable = treasures;
		treasureLoc = new int[l][l]; // the 2 dimensions are equal to each other
		players.clear(); //empty the player list
		isPlaying = false;
		
		//randomly allocate the treasures
		allocTreasures();
	}	
	
	/*
	 * Set methods
	 */
	public void setLength(int val)
	{
		length = val;
	}
	
	public void setNumOfTreasure(int val)
	{
		numOfTreasures = val;
	}
/*	
	public void setTreasureLoc(int[][] arr)
	{
		//declare the 1st dimension
		treasureLoc = new int[arr.length][];
		
		//declare the 2nd dimension and copy values over
		for (int i=0;i<arr.length;i++)
		{
			treasureLoc[i] = new int[arr[i].length];
			
			// copy values
			for  (int j=0;j<treasureLoc[i].length;j++)
			{
				treasureLoc[i][j] = arr[i][j];
			}
		}
	}
*/	
	public void setTreasureLocVal(int x, int y, int val)
	{
		if (treasureLoc == null)
			return;
		
		if (x > getLength() || y > getLength())
			return;
		
		treasureLoc[x][y] = val;
	}
	
	public void setIsPlaying(boolean val)
	{
		isPlaying = val;
	}
	
	public void setPlayers(ArrayList<Player> list)
	{
		players.clear();
		players.addAll(list);
	}
	
	public void setTreaAvailable(int val)
	{
		treaAvailable = val;
	}
	
	/**
	 * Get methods
	 */
	public int getLength()
	{
		return length;
	}
	
	public int getNumOfTreasures()
	{
		return numOfTreasures;
	}
	
	public int getTreaAvailable()
	{
		return treaAvailable;
	}
	
	public int[][] getTreasureLoc()
	{
		return treasureLoc;
	}
	
	public int getTreasureLocVal(int x, int y)
	{
		if (treasureLoc == null)
			return -1 ;
		
		if (x > getLength() || y > getLength())
			return -1;
		
		return treasureLoc[x][y];
	}
	
	public boolean getIsPlaying()
	{
		return isPlaying;
	}
	
	public ArrayList<Player> getPlayers()
	{
		return players;
	}
	
	
	/**
	 * Randomly allocate treasures into the maze with N*N grid
	 */
	private boolean allocTreasures()
	{
		if (getTreasureLoc() == null) 
			return false;
		
		if (getLength() == 0)
			return false;
		
		int count = 0;
		int treaAllocated = 0;
		Random rand = new Random();
		
		while (count < getLength() * getLength() 
				&& treaAllocated < getNumOfTreasures())
		{
			//System.out.println("count="+count);
			int x = rand.nextInt(getLength());
			int y = rand.nextInt(getLength());
			int val = rand.nextInt(getNumOfTreasures()/(getLength() * getLength()));
			
			if (val + treaAllocated > getNumOfTreasures())
			{
				count++;
				continue;
			}
			
			int oldVal = getTreasureLocVal(x,y);
			
			if (oldVal < 0)
			{
				// need to clear everything?
				return false;
			}
			else
			{
				setTreasureLocVal(x,y,oldVal + val);
				treaAllocated += val;
			}					
			count++;
		}
		
		//this is to finish the allocation from the previous steps
		if (treaAllocated < getNumOfTreasures())
		{
			int x = rand.nextInt(getLength());
			int y = rand.nextInt(getLength());
			int val = getNumOfTreasures() - treaAllocated;
			
			int oldVal = getTreasureLocVal(x,y);
			
			if (oldVal < 0)
			{
				// need to clear everything?
				return false;
			}
			else
			{
				setTreasureLocVal(x,y,oldVal + val);
			}
		}
	
		return false;
	}
	
	/**
	 * Print the treasures in the maze
	 */
	public void printMaze()
	{
		if (treasureLoc == null)
			System.out.println("The Maze is NULL");
		
		if (getLength() == 0)
		{
			System.out.println("Maze's length is ZERO");
		}
		
		for (int i=0;i<treasureLoc.length;i++)
		{
			for (int j=0; j<treasureLoc[0].length; j++)
			{
				System.out.print(getTreasureLocVal(i,j)+" ");
			}
			
			System.out.println();
		}
	}	
	
	/**
	 * Quit a player?
	 */
	public boolean quitGame(Player p)
	{
		if (!isPlaying)
			return false;
		
		//Check if it's the valid player
		if (!players.contains(p))
			return false;
		
		//If the player quit in the middle of the game, change his position to (-1,-1)
		//His data still remains in the server until the game is over
		p.setXPos(-1);
		p.setYPos(-1);
		
		return true;
	}
	
	/**
	 * Move position for a player
	 */
	public boolean movePlayer(String dir, int numOfCells, Player p)
	{
		if (dir == null)
			return false;
		
		if (!isPlaying)
			return false;
		
		//Check if the move direction is valid
		if (!dir.equalsIgnoreCase(south) && !dir.equalsIgnoreCase(north)
				&& !dir.equalsIgnoreCase(east) && !dir.equalsIgnoreCase(west)
				&& !dir.equalsIgnoreCase(noMove))
		{
			return false;
		}
		
		//Check if it's the valid player
		if (!players.contains(p))
			return false;
		
		//Check if the number of cells to move is valid
		if (numOfCells > getLength())
			return false;
		
		//To do the actual move
		if (dir.equalsIgnoreCase(north))
		{
			int y = p.getYPos();
			if (y - numOfCells < 0)
				return false;
			else // it's ok to move
			{
				int newPos = y - numOfCells;
				int treasures = getTreasureLocVal(p.getXPos(), newPos);
				
				if (isPosAvail(p.getXPos(),newPos))
				{
					p.setYPos(newPos);
					p.addTreaCollected(treasures); //will add on to the existing value
					treaAvailable -= treasures;
					setTreasureLocVal(p.getXPos(), newPos, 0);
				}
				else 
					return false;
			}
		}
		else if (dir.equalsIgnoreCase(south))
		{
			int y = p.getYPos();
			if (y + numOfCells > getLength())
				return false;
			else // it's ok to move
			{
				int newPos = y + numOfCells;
				int treasures = getTreasureLocVal(p.getXPos(), newPos);
				
				if (isPosAvail(p.getXPos(),newPos))
				{
					p.setYPos(newPos);
					p.addTreaCollected(treasures); //will add on to the existing value
					treaAvailable -= treasures;
					setTreasureLocVal(p.getXPos(), newPos, 0);
				}
				else 
					return false;				
			}
		}
		else if (dir.equalsIgnoreCase(west))
		{
			int x = p.getXPos();
			if (x - numOfCells < 0)
				return false;
			else
			{
				int newPos = x - numOfCells;
				int treasures = getTreasureLocVal(newPos, p.getYPos());
				
				if (isPosAvail(newPos,p.getYPos()))
				{
					p.setXPos(newPos);
					p.addTreaCollected(treasures);
					treaAvailable -= treasures;
					setTreasureLocVal(newPos, p.getYPos(), 0);
				}
				else
					return false;
			}
		}
		else if (dir.equalsIgnoreCase(east))
		{
			int x = p.getXPos();
			if (x + numOfCells > getLength())
				return false;
			else
			{
				int newPos = x + numOfCells;
				int treasures = getTreasureLocVal(newPos, p.getYPos());
				
				if (isPosAvail(newPos,p.getYPos()))
				{
					p.setXPos(newPos);
					p.addTreaCollected(treasures);
					treaAvailable -= treasures;
					setTreasureLocVal(newPos, p.getYPos(), 0);
				}
				else
					return false;
			}
		}			
		checkGameOver();
		return true;
	}
	
	/**
	 * Game over when all the treasures are collected
	 */
	private void checkGameOver()
	{
		if (treaAvailable <= 0)
			isPlaying = false;
	}
	
	private boolean isPosAvail(int x, int y)
	{
		for (int i=0; i < players.size(); i++)
		{
			Player p = players.get(i);
			if (p.getXPos() == x && p.getYPos() == y)
				return false;
		}
		
		return true;
	}
	
	public String gameState()
	{
		String text = "=====================GAME STATE============================_";
		
		if (isPlaying)
		{			
			text += "Collected treasures: "+(numOfTreasures - treaAvailable)+"_"; 
			text += "Available treasures: "+treaAvailable+"_";
			text += "Total players: "+players.size()+"_";
			text += "List of players in format of (x-pos,y-pos,treasures collected):_";
			for (int i=0;i<players.size();i++)
			{
				Player p = players.get(i);
				text += "("+p.getXPos()+","+p.getYPos()+","+p.getTreaCollected()+") ";
			}
		}
		else
		{
			text += "Game is over! All "+numOfTreasures+" treasures are collected!_"; 
			text += "Total players: "+players.size()+"_";
			text += "List of players in format of (x-pos,y-pos,treasures collected):_";
			for (int i=0;i<players.size();i++)
			{
				Player p = players.get(i);
				text += "("+p.getXPos()+","+p.getYPos()+","+p.getTreaCollected()+") ";
			}
		}
		
		return text;
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Maze m = new Maze(10,100);
		m.printMaze();
	}

}
